package jp.dodododo.dao.types;

import static jp.dodododo.janerics.GenericsTypeUtil.*;

import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.CharBuffer;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class JavaTypes<T> implements JavaType<T> {

	public static final JavaType<String> STRING = new JavaTypes<String>() {
		@Override
		public String getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getString(columnIndex);
		}

		@Override
		public String getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getString(columnLabel);
		}

		@Override
		public String convert(Object value) {
			return value.toString();
		}
	};

	public static final JavaType<StringBuffer> STRING_BUFFER = new JavaTypes<StringBuffer>() {
		@Override
		public StringBuffer getValue(ResultSet rs, int columnIndex) throws SQLException {
			String string = rs.getString(columnIndex);
			if (string == null) {
				return null;
			}
			return new StringBuffer(string);
		}

		@Override
		public StringBuffer getValue(ResultSet rs, String columnLabel) throws SQLException {
			String string = rs.getString(columnLabel);
			if (string == null) {
				return null;
			}
			return new StringBuffer(string);
		}

		@Override
		public StringBuffer convert(Object value) {
			return new StringBuffer(value.toString());
		}
	};

	public static final JavaType<StringBuilder> STRING_BUILDER = new JavaTypes<StringBuilder>() {
		@Override
		public StringBuilder getValue(ResultSet rs, int columnIndex) throws SQLException {
			String string = rs.getString(columnIndex);
			if (string == null) {
				return null;
			}
			return new StringBuilder(string);
		}

		@Override
		public StringBuilder getValue(ResultSet rs, String columnLabel) throws SQLException {
			String string = rs.getString(columnLabel);
			if (string == null) {
				return null;
			}
			return new StringBuilder(string);
		}

		@Override
		public StringBuilder convert(Object value) {
			return new StringBuilder(value.toString());
		}
	};

	public static final JavaType<CharBuffer> CHAR_BUFFER = new JavaTypes<CharBuffer>() {
		@Override
		public CharBuffer getValue(ResultSet rs, int columnIndex) throws SQLException {
			String string = rs.getString(columnIndex);
			if (string == null) {
				return null;
			}
			return CharBuffer.wrap(string);
		}

		@Override
		public CharBuffer getValue(ResultSet rs, String columnLabel) throws SQLException {
			String string = rs.getString(columnLabel);
			if (string == null) {
				return null;
			}
			return CharBuffer.wrap(string);
		}

		@Override
		public CharBuffer convert(Object value) {
			return CharBuffer.wrap(value.toString());
		}
	};

	public static final JavaType<Character> CHARACTER = new JavaTypes<Character>() {
		@Override
		public Character getValue(ResultSet rs, int columnIndex) throws SQLException {
			String string = rs.getString(columnIndex);
			if (string == null) {
				return null;
			}
			return string.charAt(0);
		}

		@Override
		public Character getValue(ResultSet rs, String columnLabel) throws SQLException {
			String string = rs.getString(columnLabel);
			if (string == null) {
				return null;
			}
			return string.charAt(0);
		}

		@Override
		public Character convert(Object value) {
			return value.toString().charAt(0);
		}
	};

	//
	public static final JavaType<Boolean> BOOLEAN = new JavaTypes<Boolean>() {
		@Override
		public Boolean getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getBoolean(columnIndex);
		}

		@Override
		public Boolean getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getBoolean(columnLabel);
		}

		@Override
		public Boolean convert(Object value) {
			if (value instanceof Number) {
				Number num = (Number) value;
				if (num.intValue() == 0) {
					return false;
				} else if (num.intValue() == 1) {
					return true;
				}
			}
			return super.convert(value);
		}
	};

	//
	public static final JavaType<Byte> BYTE = new JavaTypes<Byte>() {
		@Override
		public Byte getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return num.byteValue();
		}

		@Override
		public Byte getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return num.byteValue();
		}

		@Override
		public Byte convert(Object value) {
			return Byte.valueOf(value.toString());
		}
	};

	//
	public static final JavaType<Integer> INTEGER = new JavaTypes<Integer>() {
		@Override
		public Integer getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return num.intValue();
		}

		@Override
		public Integer getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return num.intValue();
		}
	};

	//
	public static final JavaType<BigDecimal> BIG_DECIMAL = new JavaTypes<BigDecimal>() {
		@Override
		public BigDecimal getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			return num;
		}

		@Override
		public BigDecimal getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			return num;
		}

		@Override
		public BigDecimal convert(Object value) {
			return new BigDecimal(value.toString());
		}
	};

	//
	public static final JavaType<BigInteger> BIG_INTEGER = new JavaTypes<BigInteger>() {
		@Override
		public BigInteger getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return BigInteger.valueOf(num.longValue());
		}

		@Override
		public BigInteger getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return BigInteger.valueOf(num.longValue());
		}

		@Override
		public BigInteger convert(Object value) {
			return BigInteger.valueOf(Long.valueOf(value.toString()));
		}
	};

	//
	public static final JavaType<Double> DOUBLE = new JavaTypes<Double>() { // {
		@Override
		public Double getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return num.doubleValue();
		}

		@Override
		public Double getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return num.doubleValue();
		}
	};

	//
	public static final JavaType<Float> FLOAT = new JavaTypes<Float>() {
		@Override
		public Float getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return num.floatValue();
		}

		@Override
		public Float getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return num.floatValue();
		}

		@Override
		public Float convert(Object value) {
			return Float.valueOf(value.toString());
		}
	};

	//
	public static final JavaType<Long> LONG = new JavaTypes<Long>() {
		@Override
		public Long getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return num.longValue();
		}

		@Override
		public Long getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return num.longValue();
		}

		@Override
		public Long convert(Object value) {
			return Long.valueOf(value.toString());
		}
	};

	//
	public static final JavaType<Number> NUMBER = new JavaTypes<Number>() {
		@Override
		public Number getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			return num;
		}

		@Override
		public Number getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			return num;
		}
	};

	//
	public static final JavaType<Short> SHORT = new JavaTypes<Short>() {
		@Override
		public Short getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return num.shortValue();
		}

		@Override
		public Short getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return num.shortValue();
		}

		@Override
		public Short convert(Object value) {
			return Short.valueOf(value.toString());
		}
	};

	//
	public static final JavaType<AtomicInteger> ATOMIC_INTEGER = new JavaTypes<AtomicInteger>() {
		@Override
		public AtomicInteger getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return new AtomicInteger(num.intValue());
		}

		@Override
		public AtomicInteger getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return new AtomicInteger(num.intValue());
		}
	};

	//
	public static final JavaType<AtomicLong> ATOMIC_LONG = new JavaTypes<AtomicLong>() {
		@Override
		public AtomicLong getValue(ResultSet rs, int columnIndex) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnIndex);
			if (num == null) {
				return null;
			}
			return new AtomicLong(num.longValue());
		}

		@Override
		public AtomicLong getValue(ResultSet rs, String columnLabel) throws SQLException {
			BigDecimal num = rs.getBigDecimal(columnLabel);
			if (num == null) {
				return null;
			}
			return new AtomicLong(num.longValue());
		}

		@Override
		public AtomicLong convert(Object value) {
			if (value instanceof Number) {
				Number num = (Number) value;
				new AtomicLong(num.longValue());
			}
			return super.convert(value);
		}
	};

	//
	public static final JavaType<Object> OBJECT = new JavaTypes<Object>() {
		@Override
		public Object getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getObject(columnIndex);
		}

		@Override
		public Object getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getObject(columnLabel);
		}

		@Override
		public String convert(Object value) {
			return value.toString();
		}
	};

	//
	public static final JavaType<Date> SQL_DATE = new JavaTypes<Date>() { // {
		@Override
		public Date getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getDate(columnIndex);
		}

		@Override
		public Date getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getDate(columnLabel);
		}
	};

	//
	public static final JavaType<Time> TIME = new JavaTypes<Time>() { // {
		@Override
		public Time getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getTime(columnIndex);
		}

		@Override
		public Time getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getTime(columnLabel);
		}
	};

	//
	public static final JavaType<Timestamp> TIMESTAMP = new JavaTypes<Timestamp>() { // {
		@Override
		public Timestamp getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getTimestamp(columnIndex);
		}

		@Override
		public Timestamp getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getTimestamp(columnLabel);
		}
	};

	//
	public static final JavaType<Calendar> CALENDAR = new JavaTypes<Calendar>() { // {
		@Override
		public Calendar getValue(ResultSet rs, int columnIndex) throws SQLException {
			Timestamp timestamp = rs.getTimestamp(columnIndex);
			if (timestamp == null) {
				return null;
			}
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(timestamp);
			return calendar;
		}

		@Override
		public Calendar getValue(ResultSet rs, String columnLabel) throws SQLException {
			Timestamp timestamp = rs.getTimestamp(columnLabel);
			if (timestamp == null) {
				return null;
			}
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(timestamp);
			return calendar;
		}

		@Override
		public Calendar convert(Object value) {
			if (value instanceof Date) {
				Date date = (Date) value;
				Calendar calendar = Calendar.getInstance();
				calendar.setTime(date);
				return calendar;
			}
			return super.convert(value);
		}
	};

	//
	public static final JavaType<InputStream> INPUT_STREAM = new JavaTypes<InputStream>() {
		@Override
		public InputStream getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getBinaryStream(columnIndex);
		}

		@Override
		public InputStream getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getBinaryStream(columnLabel);
		}
	};

	//
	public static final JavaType<byte[]> BYTE_ARRAY = new JavaTypes<byte[]>() {
		@Override
		public byte[] getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getBytes(columnIndex);
		}

		@Override
		public byte[] getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getBytes(columnLabel);
		}
	};

	public static final JavaType<java.sql.SQLXML> SQLXML = new JavaTypes<java.sql.SQLXML>() {
		@Override
		public java.sql.SQLXML getValue(ResultSet rs, int columnIndex) throws SQLException {
			return rs.getSQLXML(columnIndex);
		}

		@Override
		public java.sql.SQLXML getValue(ResultSet rs, String columnLabel) throws SQLException {
			return rs.getSQLXML(columnLabel);
		}
	};

	public static final JavaType<Object> NULL = new JavaTypes<Object>() {
		@Override
		public Object getValue(ResultSet rs, int columnIndex) throws SQLException {
			return null;
		}

		@Override
		public Object getValue(ResultSet rs, String columnLabel) throws SQLException {
			return null;
		}

		@Override
		public Object convert(Object value) {
			return null;
		}
	};

	public T getValue(ResultSet rs, int columnIndex) throws SQLException {
		throw new UnsupportedOperationException();
	}

	public T getValue(ResultSet rs, String columnLabel) throws SQLException {
		throw new UnsupportedOperationException();
	}

	public T convert(Object value) {
		throw new UnsupportedOperationException();
	}

	@SuppressWarnings("unchecked")
	public Class<T> getType() {
		return (Class<T>) getRawClass(getTypeParameter(this.getClass().getGenericSuperclass(), 0));
	}
}
